//
//  RulesManager.m
//  Pennyworth
//
//  Created by Chris Karr on 7/27/08.
//  Copyright 2008 Northwestern University. All rights reserved.
//

#import "RulesManager.h"
#import "RulesRowTemplate.h"
#import "AppDelegate.h"
#import "AnalyticsLogger.h"

#define NEW_RULE @"New Rule"
#define RULE_NAME @"name"
#define RULE_KIND @"kind"
#define ACTIVITY @"Activity"
#define AWAY @"Away"
#define RULE_VALUE @"value"

#define RULE_PREDICATE @"rule"

#define SAVED_RULES @"Pennyworth_Saved_Rules"

@implementation RulesManager

@synthesize rules;
@synthesize priorPredictions;

static RulesManager * sharedRulesManager = nil;

+ (RulesManager *) sharedRulesManager
{
	return sharedRulesManager;
}

- (NSPredicate *) normalizePredicate:(NSPredicate *) dirtyPredicate
{
	if (dirtyPredicate == nil)
		return dirtyPredicate;
	
	NSPredicate * predicate = dirtyPredicate;
	
	if ([dirtyPredicate isKindOfClass:[NSComparisonPredicate class]])
	{
		NSComparisonPredicate * comp = (NSComparisonPredicate *) dirtyPredicate;
		
		NSString * value = [[comp rightExpression] constantValue];
		
		value = [value stringByReplacingOccurrencesOfString:@"\""
												 withString:@""];
		
		value = [value stringByReplacingOccurrencesOfString:@"\\"
												 withString:@""];
		
		NSExpression * right = [NSExpression expressionForConstantValue:value];

		NSPredicateOperatorType operator = [comp predicateOperatorType];
		
		if (operator == NSLessThanOrEqualToPredicateOperatorType || operator == NSGreaterThanOrEqualToPredicateOperatorType)
		{
			NSNumber * number = [NSNumber numberWithDouble:[value doubleValue]];
			right = [NSExpression expressionForConstantValue:number];
		}
		
		predicate = [NSComparisonPredicate predicateWithLeftExpression:[comp leftExpression] 
													   rightExpression:right
															  modifier:NSDirectPredicateModifier
																  type:[comp predicateOperatorType]
															   options:[comp options]];
	}
	else if ([dirtyPredicate isKindOfClass:[NSCompoundPredicate class]])
	{
		NSCompoundPredicate * compound = (NSCompoundPredicate *) dirtyPredicate;
		
		NSMutableArray * clean = [NSMutableArray array];
		
		for (NSPredicate * p in [compound subpredicates])
			[clean addObject:[self normalizePredicate:p]];
		
		if ([compound compoundPredicateType] == NSNotPredicateType)
			predicate = [NSCompoundPredicate notPredicateWithSubpredicate:[clean lastObject]];
		else if ([compound compoundPredicateType] == NSAndPredicateType)
			predicate = [NSCompoundPredicate andPredicateWithSubpredicates:clean];
		else
			predicate = [NSCompoundPredicate orPredicateWithSubpredicates:clean];
	}
	
	return predicate;
}

- (NSString *) labelForKey:(NSString *) key forFeatures:(NSArray *) features
{
	NSMutableDictionary * world = [NSMutableDictionary dictionary];

	for (NSDictionary * obs in features)
	 	[world setValue:[obs valueForKey:FEATURE_OBSERVATION] forKey:[obs valueForKey:FEATURE_SENSOR]];

	NSString * label = nil;

	for (NSDictionary * rule in self.rules)
	{
		if (label == nil && [[rule valueForKey:RULE_KIND] isEqualToString:key])
		{
			NSPredicate * predicate = [rule valueForKey:RULE_PREDICATE];
			
			if (predicate != nil && [predicate evaluateWithObject:world])
				label = [rule valueForKey:RULE_VALUE];
		}
	}

	return label;
}

- (void) save:(NSNotification *) theNote
{
	NSData * data = [NSKeyedArchiver archivedDataWithRootObject:self.rules];
	
	[[NSUserDefaults standardUserDefaults] setObject:data forKey:SAVED_RULES];
}

- (void) awakeFromNib
{
	[rulesController addObserver:self forKeyPath:@"selection" options:0 context:NULL];
}

- (void) observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
	NSDictionary * rule = [[rulesController selectedObjects] lastObject];

	if ([keyPath isEqual:@"selection"])
	{
		[ruleEditor setObjectValue:[rule valueForKey:RULE_PREDICATE]];

		[rule addObserver:self forKeyPath:RULE_KIND options:0 context:NULL];
	}
	else if ([keyPath isEqual:RULE_KIND])
	{
		[self willChangeValueForKey:@"values"];
		[self.priorPredictions removeObjects:[self.priorPredictions arrangedObjects]];
		
		NSString * which = [rule valueForKey:RULE_KIND];
		
		NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
		NSArray * values = [NSArray array];
		
		if ([which isEqual:@"Activity"])
			values = [defaults objectForKey:@"Saved Activities"];
		else if ([which isEqual:@"Social Context"])
			values = [defaults objectForKey:@"Saved Socials"];
		else if ([which isEqual:@"Location"])
			values = [defaults objectForKey:@"Saved Locations"];
		
		[self.priorPredictions addObjects:values];
		[self didChangeValueForKey:@"values"];
	}
}

- (RulesManager *) init
{
	if (self == [super init])
	{
		NSData * rulesData = [[NSUserDefaults standardUserDefaults] objectForKey:SAVED_RULES];
	
		if (rulesData != nil)
			self.rules = [NSKeyedUnarchiver unarchiveObjectWithData:rulesData];
	
		if (self.rules == nil)
			self.rules = [NSMutableArray array];
	
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(save:) name:TERMINATE_NOTIFICATION object:nil];

		self.priorPredictions = [[NSArrayController alloc] init];
		
		sharedRulesManager = self;
	}
	
	return self;
}

- (NSArray *) kinds
{
	return [NSArray arrayWithObjects:@"Activity", @"Location", @"Social Context", nil];
}

- (NSArray *) values
{
	return [[self.priorPredictions arrangedObjects] sortedArrayUsingSelector:@selector(compare:)];
}

- (IBAction) saveRule:(id) sender
{
	NSDictionary * postObject = [NSDictionary dictionaryWithObjectsAndKeys:@"Saved Rule", ANALYTIC_NAME, @"", ANALYTIC_VALUE, nil];
	[[NSNotificationCenter defaultCenter] postNotificationName:ANALYTIC_EVENT object:postObject];

	[[[rulesController selectedObjects] lastObject] setValue:[self normalizePredicate:[ruleEditor objectValue]] forKey:RULE_PREDICATE];
}

- (IBAction) add:(id) sender
{
	NSDictionary * postObject = [NSDictionary dictionaryWithObjectsAndKeys:@"Created New Rule", ANALYTIC_NAME, @"", ANALYTIC_VALUE, nil];
	[[NSNotificationCenter defaultCenter] postNotificationName:ANALYTIC_EVENT object:postObject];

	NSMutableDictionary * rule = [NSMutableDictionary dictionary];
	[rule setValue:NEW_RULE forKey:RULE_NAME];
	[rule setValue:ACTIVITY forKey:RULE_KIND];
	[rule setValue:AWAY forKey:RULE_VALUE];
	
	NSPredicate * predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObject:
	 							[NSPredicate predicateWithFormat:@"(%K <= 0.25)", @"User Activity Level"]]];
	
	[rule setValue:predicate forKey:RULE_PREDICATE];
	
	[rulesController addObject:rule];
	[rulesController setSelectedObjects:[NSArray arrayWithObject:rule]];
}

@end
